﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="RejectionEngine.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the RejectionEngine type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace BusinessEngines
{
  using System;
  using System.Collections.Generic;
  using BusinessEngineInterfaces;
  using Entities;
  using Exceptions;
  using ServiceInterfaces;
  using UnityRepository.Interfaces;

  public class RejectionEngine : IRejectionEngine
  {
    private readonly IDocumentService documentService;
    private readonly IRejectionRepository rejectionRepository;

    public RejectionEngine(IDocumentService documentService, IRejectionRepository rejectionRepository)
    {
      this.documentService = documentService;
      this.rejectionRepository = rejectionRepository;
    }

    private enum RejectionResult
    {
      Approvable,
      AlreadyApproved,
      AlreadyRejected
    }

    public void RejectDocument(String userName, String documentId)
    {
      var document = this.documentService.GetDocument(documentId);
      if (document == null)
      {
        throw new UnableToRetrieveDocumentException(String.Format("Unable to retrieve document {0}", documentId));
      }

      if (document.Approval != null)
      {
        throw new DocumentAlreadyApprovedException("Document is already approved");
      }

      if (document.Rejection != null)
      {
        throw new DocumentAlreadyRejectedException("Document is already rejected");
      }

      var rejection = new Rejection
      {
        RejectedBy = userName,
        RejectionDate = DateTime.Now,
        DocumentId = document.Id
      };

      this.rejectionRepository.Create(rejection);
    }

    public BatchApprovalResults RejectGrid(String grid, String userName)
    {
      IList<Document> gridDocuments = this.documentService.GetDocumentsWithApprovalAndRejectionEntities(grid);

      var batchRejectionResults = new BatchApprovalResults();
      var rejectionItems = new List<Rejection>();

      foreach (Document document in gridDocuments)
      {
        this.AttemptRejectionAndAddResultsToLists(userName, document, batchRejectionResults, rejectionItems);
      }

      this.rejectionRepository.Create(rejectionItems);

      return batchRejectionResults;
    }

    public BatchApprovalResults RejectBasket(String userName, String cartId)
    {
      IList<Document> basketDocuments = this.documentService.GetDocumentsForBasket(cartId);

      var batchRejectionResults = new BatchApprovalResults();
      var rejectionItems = new List<Rejection>();

      foreach (Document document in basketDocuments)
      {
        this.AttemptRejectionAndAddResultsToLists(userName, document, batchRejectionResults, rejectionItems);
      }

      this.rejectionRepository.Create(rejectionItems);

      return batchRejectionResults;
    }

    private void AttemptRejectionAndAddResultsToLists(
      String userName,
      Document document,
      BatchApprovalResults batchApprovalResults,
      ICollection<Rejection> approvalItems)
    {
      RejectionResult rejectionResult = this.CanDocumentBeRejected(document);

      switch (rejectionResult)
      {
        case RejectionResult.Approvable:
          batchApprovalResults.ApprovedDocuments.Add(document.DocumentId);
          approvalItems.Add(new Rejection { RejectedBy = userName, RejectionDate = DateTime.Now, DocumentId = document.Id });
          break;
        case RejectionResult.AlreadyApproved:
          batchApprovalResults.AlreadyApprovedDocuments.Add(document.DocumentId);
          break;
        case RejectionResult.AlreadyRejected:
          batchApprovalResults.AlreadyRejectedDocuments.Add(document.DocumentId);
          break;
      }
    }

    private RejectionResult CanDocumentBeRejected(Document document)
    {
      if (document.Approval != null)
      {
        return RejectionResult.AlreadyApproved;
      }

      if (document.Rejection != null)
      {
        return RejectionResult.AlreadyRejected;
      }

      return RejectionResult.Approvable;
    }
  }
}
